package parser_protobuf3

import (
	"fmt"
	"path/filepath"
	"strconv"
	"strings"

	"gitee.com/woodpile/wgs.ths.code.generate/definition"
	"github.com/jhump/protoreflect/desc" //cspell: disable-line
)

type ProtoOptions struct {
	Messages     map[string]*ProtoOption
	messageAsNum map[string]*ProtoOption

	Fields     map[string]*ProtoOption
	fieldAsNum map[string]*ProtoOption

	OneOfs     map[string]*ProtoOption
	oneOfAsNum map[string]*ProtoOption
}

func newProtoOptions() *ProtoOptions {
	return &ProtoOptions{
		Messages:     make(map[string]*ProtoOption),
		messageAsNum: make(map[string]*ProtoOption),
		Fields:       make(map[string]*ProtoOption),
		fieldAsNum:   make(map[string]*ProtoOption),
		OneOfs:       make(map[string]*ProtoOption),
		oneOfAsNum:   make(map[string]*ProtoOption),
	}
}

type ProtoOption struct {
	Name   string
	Number int32
}

func (p *Parser) parseOptions(ctx *definition.Context) error {
	fParseFile := func(dir, filename string) error {
		if definition.GlobalConfig.Verbose {
			fmt.Println("ParseFile ", filepath.Join(dir, filename))
		}

		df := p.getOrNewDFFile(ctx, dir, filename)
		arrProtoFile, err := protoParser.ParseFiles(df.Path)
		if err != nil {
			return fmt.Errorf("proto parse file %s failed: %w", df.Path, err)
		}
		for _, pf := range arrProtoFile {
			if err := p.parseFileOptions(pf); err != nil {
				return fmt.Errorf("parse file %s content failed: %w", df.Path, err)
			}
		}
		return nil
	}
	for _, dir := range definition.GlobalConfig.Parser.Dir {
		if err := definition.ForeachFileRecursively(dir, p.filterFile, fParseFile); err != nil {
			return err
		}
	}
	return nil
}

func (p *Parser) parseFileOptions(pf *desc.FileDescriptor) error {
	for _, pe := range pf.GetExtensions() {
		om := &ProtoOption{}
		om.Name = pe.GetName()
		om.Number = pe.GetNumber()

		switch pe.AsFieldDescriptorProto().GetExtendee() { //cspell: disable-line
		case ".google.protobuf.MessageOptions":
			p.ProtoOptions.Messages[om.Name] = om
			p.ProtoOptions.messageAsNum[strconv.FormatInt(int64(om.Number), 10)] = om
		case ".google.protobuf.FieldOptions":
			p.ProtoOptions.Fields[om.Name] = om
			p.ProtoOptions.fieldAsNum[strconv.FormatInt(int64(om.Number), 10)] = om
		case ".google.protobuf.OneofOptions":
			p.ProtoOptions.OneOfs[om.Name] = om
			p.ProtoOptions.oneOfAsNum[strconv.FormatInt(int64(om.Number), 10)] = om
		default:
			fmt.Printf("unknown extendee: %s\n", pe.AsFieldDescriptorProto().GetExtendee()) //cspell: disable-line
		}
	}
	return nil
}

func (p *Parser) parseProtoOptionUsing(str string) map[string]string {
	if str == "" {
		return nil
	}
	m := make(map[string]string)

	arrOp := strings.Split(str, " ")
	for _, op := range arrOp {
		arrOpKV := strings.SplitN(op, ":", 2)
		if len(arrOpKV) != 2 {
			continue
		}
		v := strings.Trim(arrOpKV[1], "\"")
		m[arrOpKV[0]] = v
	}

	return m
}
